<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>北极光之夜。</title>
        <style>
            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
            }
            body {
                height: 100vh;
                display: flex;
                justify-content: center;
                align-items: center;
                background-color: rgb(0, 0, 0);
            }
        </style>
    </head>
    <body>
        <canvas id="canvas"></canvas>

        <script>
            // 获取画布
            var canvas = document.getElementById('canvas');
            var ctx = canvas.getContext('2d');
            // 先在画布绘制一串文字，大小可以自己调，我这30px，6个字，大概搞个180px*30px的大小，背景色为黑色
            ctx.font = '30px fangsong';
            ctx.fillStyle = 'rgb(0, 0, 0)';
            ctx.fillText('北极光之夜。', 0, 30, 180);
            // getImageData()可以获取画布上指定矩形的像素数据，把那上面文字所在区域的的像素保存起来
            var pix = ctx.getImageData(0, 0, 180, 35);
            /* 
            getImageData() 复制画布上指定矩形的像素数据。
            getImageData() 方法返回 ImageData 对象，该对象拷贝了画布指定矩形的像素数据。
            对于 ImageData 对象中的每个像素，都存在着四方面的信息，即 RGBA 值：
            R - 红色 (0-255)
            G - 绿色 (0-255)
            B - 蓝色 (0-255)
            A - alpha 通道 (0-255; 0 是透明的，255 是完全可见的)
            color/alpha 以数组形式存在，并存储于 ImageData 对象的 data 属性中。
            */
            // 画布动态适应屏幕大小
            window.addEventListener('resize', canvasResize);
            function canvasResize() {
                canvas.width = window.innerWidth;
                canvas.height = window.innerHeight;
            }
            canvasResize();
            // 这是等会绘制粒子文字的处于窗口大概位置
            var cx = canvas.width / 2 - (180 * 5) / 2;
            var cy = canvas.height / 2 - (35 * 5) / 2;
            //定义一个Particle类
            class Particle {
                constructor() {
                    this.arr = []; //定义一个数组，存放每个粒子的信息
                }
                //初始化每个粒子信息
                init() {
                    for (let i = 0; i < pix.data.length / 4; i++) {
                        //循环，像素是以4个数据为一组的
                        this.arr.push({
                            x: i % 180, //文字像素本该的处于的水平位置
                            y: i / 180, //文字像素本该的处于的垂直位置
                            alpha: pix.data[i * 4 + 3], // 透明度
                            mx: Math.random() * canvas.width, //给个随机水平位置，粒子运动 mx ---》x
                            my: Math.random() * canvas.height, //给个随机垂直位置，粒子运动 my ---》y
                            radius: Math.random() * 3, //粒子半径
                            speed: Math.random() * 40 + 40, //粒子速度
                            color: `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255},${pix.data[i * 4 + 3]}`, //粒子随机颜色
                        });
                    }
                }
                //绘制
                draw() {
                    //遍历arr数组
                    this.arr.forEach((item) => {
                        //下面就是绘制每个小圆了
                        ctx.beginPath();
                        ctx.fillStyle = item.color;
                        ctx.arc(item.mx, item.my, item.radius, 0, Math.PI * 2, false);
                        ctx.fill();
                    });
                }
                //粒子位置更新
                update() {
                    for (let i = 0; i < this.arr.length; i++) {
                        //粒子运动 mx ---》x ， my ---》y ， 采用缓动动画原理
                        //当然，如果粒子移动到原先的位置的话，岂不是太小，就文字就30px大小，所以位置改为x*5+cx，y*5+cy
                        this.arr[i].mx = this.arr[i].mx + (this.arr[i].x * 5 + cx - this.arr[i].mx) / this.arr[i].speed;
                        this.arr[i].my = this.arr[i].my + (this.arr[i].y * 5 + cy - this.arr[i].my) / this.arr[i].speed;
                    }
                }
            }
            // new一个 Particle，然后初始化
            const particle = new Particle();
            particle.init();

            // 窗口大小改变重新初始化
            window.addEventListener('resize', function () {
                particle.arr = [];
                particle.init();
                cx = canvas.width / 2 - (180 * 5) / 2;
                cy = canvas.height / 2 - (35 * 5) / 2;
            });

            // 执行粒子运动动画
            function step() {
                ctx.fillStyle = 'rgba(0,0,0,0.1)';
                ctx.fillRect(0, 0, canvas.width, canvas.height);
                particle.draw();
                particle.update();

                window.requestAnimationFrame(step);
            }
            window.requestAnimationFrame(step);
            // window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画，并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
        </script>
    </body>
</html>
